#!/bin/bash
# valheim-server starts the Valheim server
# binary either vanilla from /opt/valheim/server
# or if VALHEIM_PLUS=true with the ValheimPlus mod
# from /opt/valheim/plus

# Include defaults and common functions
. /usr/local/etc/valheim/defaults
. /usr/local/etc/valheim/common

export SteamAppId=892970

valheim_server_pid=-1
timeout=29
kill_signal=TERM
valheim_server_args=()
crossplay_args=()
mod_name=none

if [ "$VALHEIM_PLUS" = true ]; then
    cd "$vp_install_path" || fatal "Could not cd $vp_install_path"
    rm -rf "$vp_install_path.old"
    export DOORSTOP_ENABLE=TRUE
    export DOORSTOP_INVOKE_DLL_PATH="./BepInEx/core/BepInEx.Preloader.dll"
    export DOORSTOP_CORLIB_OVERRIDE_PATH="./unstripped_corlib"
    export LD_LIBRARY_PATH="$vp_install_path/linux64/:$vp_install_path/doorstop_libs/"
    SERVER_LD_PRELOAD=libdoorstop_x64.so
    valheim_server="$vp_install_path/valheim_server.x86_64"
    if [ -n "$SERVER_PASS" ]; then
        valheim_server_args+=(-password "$SERVER_PASS")
    fi
    mod_name="ValheimPlus"
elif [ "$BEPINEX" = true ]; then
    cd "$bepinex_install_path" || fatal "Could not cd $bepinex_install_path"
    rm -rf "$bepinex_install_path.old"
    export DOORSTOP_ENABLE=TRUE
    export DOORSTOP_INVOKE_DLL_PATH="./BepInEx/core/BepInEx.Preloader.dll"
    export DOORSTOP_CORLIB_OVERRIDE_PATH="./unstripped_corlib"
    export LD_LIBRARY_PATH="$bepinex_install_path/linux64/:$bepinex_install_path/doorstop_libs/"
    SERVER_LD_PRELOAD=libdoorstop_x64.so
    valheim_server="$bepinex_install_path/valheim_server.x86_64"
    if [ -n "$SERVER_PASS" ]; then
        valheim_server_args+=(-password "$SERVER_PASS")
    fi
    mod_name="BepInEx"
else
    cd "$valheim_install_path" || fatal "Could not cd $valheim_install_path"
    export LD_LIBRARY_PATH="$valheim_install_path/linux64/"
    SERVER_LD_PRELOAD=""
    valheim_server="$valheim_install_path/valheim_server.x86_64"
    valheim_server_args+=(-password "$SERVER_PASS")
fi

if [ "$CROSSPLAY" = true ]; then
    valheim_server_args+=(-crossplay)
fi

main() {
    wait_for_server_download
    run_server
}


wait_for_server_download() {
    while :; do
        if [ -f "$valheim_server" ]; then
            break
        else
            debug "Valheim Server is not yet downloaded - waiting"
            sleep 7
        fi
    done
}


# This runs after server start and waits for the
# config files to either exist or for the server
# to create them so their access permissions can
# be adjusted to the proper values.
ensure_permissions_after_start() {
    while :; do
        if [ -f "/config/adminlist.txt" ]; then
            ensure_permissions
            break
        else
            sleep 4
        fi
    done
}


wait_for_server_listening() {
    pre_server_listening_hook

    while :; do
        if server_is_listening; then
            update_server_status running
            debug "Server is now listening on UDP query port $SERVER_QUERY_PORT"
            post_server_listening_hook
            break
        else
            debug "Waiting for server to listen on UDP query port $SERVER_QUERY_PORT"
            sleep 5
        fi
    done
}


run_server() {
    pre_server_run_hook
    info "Running Valheim Server"
    debug "Server config is name: $SERVER_NAME, port: $SERVER_PORT/udp, world: $WORLD_NAME, public: $SERVER_PUBLIC, mod: $mod_name"
    update_server_status starting

    chmod +x "$valheim_server"
    # shellcheck disable=SC2086
    LD_PRELOAD=$SERVER_LD_PRELOAD "$valheim_server" -nographics -batchmode -name "$SERVER_NAME" -port "$SERVER_PORT" -world "$WORLD_NAME" -public "$SERVER_PUBLIC" "${valheim_server_args[@]}" $SERVER_ARGS > >(filter) 2>&1 &
    valheim_server_pid=$!
    unset LD_LIBRARY_PATH
    unset LD_PRELOAD
    echo $valheim_server_pid > "$valheim_server_pidfile"

    ensure_permissions_after_start &
    permissions_wait_pid=$!
    wait_for_server_listening &
    wait_for_server_listening_pid=$!

    wait $valheim_server_pid
    debug "Valheim server with PID $valheim_server_pid stopped"
    update_server_status stopped
    post_server_run_hook

    cleanup
    post_server_shutdown_hook
    info "Shutdown complete"
    exit 0
}


cleanup() {
    if [ -n "${permissions_wait_pid:-}" ] && [ -d "/proc/$permissions_wait_pid" ]; then
        kill -TERM $permissions_wait_pid
    fi
    if [ -n "${wait_for_server_listening_pid:-}" ] && [ -d "/proc/$wait_for_server_listening_pid" ]; then
        kill -TERM $wait_for_server_listening_pid
    fi
    clear_lock "$valheim_server_pidfile"
}


# This function gets the servers stdout/stderr
# output piped into. It allows for filtering
# of unwanted debug log messages.
filter() {
    unset LD_LIBRARY_PATH
    unset LD_PRELOAD
    "$cmd_valheim_logfilter" -logtostderr -v "$VALHEIM_LOG_FILTER_VERBOSE"
}


pre_server_run_hook() {
    if [ -n "$PRE_SERVER_RUN_HOOK" ]; then
        info "Running pre server run hook: $PRE_SERVER_RUN_HOOK"
        eval "$PRE_SERVER_RUN_HOOK"
    fi
}


post_server_run_hook() {
    if [ -n "$POST_SERVER_RUN_HOOK" ]; then
        info "Running post server run hook: $POST_SERVER_RUN_HOOK"
        eval "$POST_SERVER_RUN_HOOK"
    fi
}

pre_server_listening_hook() {
     if [ -n "$PRE_SERVER_LISTENING_HOOK" ]; then
        info "Running pre server listening hook: $PRE_SERVER_LISTENING_HOOK"
        eval "$PRE_SERVER_LISTENING_HOOK"
    fi
}

post_server_listening_hook() {
    if [ -n "$POST_SERVER_LISTENING_HOOK" ]; then
        info "Running post server listening hook: $POST_SERVER_LISTENING_HOOK"
        eval "$POST_SERVER_LISTENING_HOOK"
    fi
}


shutdown() {
    debug "Received signal to shut down valheim-server"
    update_server_status stopping
    if [ $valheim_server_pid -eq -1 ]; then
        debug "Valheim server is not running yet - aborting startup"
        update_server_status stopped
        exit
    fi
    pre_server_shutdown_hook
    info "Shutting down Valheim server with PID $valheim_server_pid"
    kill -INT $valheim_server_pid
    shutdown_timeout=$(($(date +%s)+timeout))
    while [ -d "/proc/$valheim_server_pid" ]; do
        if [ "$(date +%s)" -gt $shutdown_timeout ]; then
            shutdown_timeout=$(($(date +%s)+timeout))
            warn "Timeout while waiting for server to shut down - sending SIG$kill_signal to PGID $valheim_server_pid"
            kill -$kill_signal $valheim_server_pid
            case "$kill_signal" in
                INT)
                    kill_signal=TERM
                    ;;
                *)
                    kill_signal=KILL
            esac
        fi
        debug "Waiting for Valheim Server with PID $valheim_server_pid to shut down"
        sleep 6
    done
}


pre_server_shutdown_hook() {
    if [ -n "$PRE_SERVER_SHUTDOWN_HOOK" ]; then
        info "Running pre server shutdown hook: $PRE_SERVER_SHUTDOWN_HOOK"
        eval "$PRE_SERVER_SHUTDOWN_HOOK"
    fi
}


post_server_shutdown_hook() {
    if [ -n "$POST_SERVER_SHUTDOWN_HOOK" ]; then
        info "Running post server shutdown hook: $POST_SERVER_SHUTDOWN_HOOK"
        eval "$POST_SERVER_SHUTDOWN_HOOK"
    fi
}


trap shutdown SIGINT SIGTERM
main
